// hwinobjbase.h

#pragma once
#ifndef __HWINOBJBASE_H__
#define __HWINOBJBASE_H__

#include "hwindef.h"
#include "hwincom.h"
#include "hwinexception.h"
#include "hwinvariant.h"


namespace harlinn
{
    namespace windows
    {
        enum class MarshalContext : DWORD
        {
            Local              = 0,
            NoSharedMem        = 1,
            DifferentMachine   = 2,
            InProc             = 3,
            CrossCtx           = 4
        };

        enum class MarshalFlags : DWORD
        {
            Normal        = 0,
            TableStrong   = 1,
            TableWeak     = 2,
            NoPing        = 4
        };
        DEFINE_ENUM_FLAG_OPERATORS(MarshalFlags)


        class Marshal : public Unknown
        {
        public:
            typedef Unknown Base;

            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(Marshal,Unknown,IMarshal,IUnknown)

            CLSID GetUnmarshalClass( REFIID riid, void *pv, MarshalContext theDestContext, MarshalFlags flags)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                CLSID result;
                auto hr = pInterface->GetUnmarshalClass(riid,pv,DWORD(theDestContext),nullptr,DWORD(flags),&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            DWORD GetMarshalSizeMax( REFIID riid, void *pv, MarshalContext theDestContext, MarshalFlags flags)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                DWORD result;
                auto hr = pInterface->GetMarshalSizeMax(riid,pv,DWORD(theDestContext),nullptr,DWORD(flags),&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }

            Marshal& MarshalInterface( IStream *pStm, REFIID riid, void *pv, MarshalContext theDestContext, MarshalFlags flags)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->MarshalInterface(pStm,riid,pv,DWORD(theDestContext),nullptr,DWORD(flags));
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            Marshal& UnmarshalInterface( IStream *pStm, REFIID riid, void **ppv)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->UnmarshalInterface(pStm,riid,ppv);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
            Marshal& ReleaseMarshalData( IStream *pStm )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->ReleaseMarshalData(pStm);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            Marshal& DisconnectObject( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->DisconnectObject( 0 );
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        };


        class Malloc : public Unknown
        {
        public:
            typedef Unknown Base;

            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(Malloc,Unknown,IMalloc,IUnknown)

            static Malloc GetMalloc()
            {
                HWIN_TRACE();
                IMalloc* ptr = nullptr;
                auto hr = CoGetMalloc(1,&ptr);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return Malloc(ptr);
            }

            void* Alloc(SIZE_T numberOfBytesToAllocate)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto result = pInterface->Alloc( numberOfBytesToAllocate );
                if(!result && numberOfBytesToAllocate)
                {
                    CheckHRESULT(E_OUTOFMEMORY);
                }
                return result;
            }
            void* Realloc(void *pv,SIZE_T newMemoryBufferSize)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto result = pInterface->Realloc(pv, newMemoryBufferSize );
                if(!result && newMemoryBufferSize)
                {
                    CheckHRESULT(E_OUTOFMEMORY);
                }
                return result;
            }
            void Free(void *pv)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                pInterface->Free(pv);
            }
            SIZE_T GetSize(void *pv)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto result = pInterface->GetSize(pv);
                return result;
            }
            int DidAlloc(void *pv)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto result = pInterface->DidAlloc(pv);
                return result;
            }
            void HeapMinimize(void)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                pInterface->HeapMinimize();
            }
        };


        class EnumUnknown : Unknown
        {
        public:
            typedef Unknown Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(EnumUnknown,Unknown,IEnumUnknown,IUnknown)

            bool Next( Unknown& theResult )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IUnknown* pElement = nullptr;
                ULONG actual = 0;
                auto hr = pInterface->Next(1,&pElement,&actual);
                if(actual)
                {
                    theResult.Reset(pElement);
                    return true;
                }
                return false;
            }
            bool Next( ULONG requestedNumberOfElements, std::vector<Unknown>& result)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IUnknown** pElements = (IUnknown**)alloca(requestedNumberOfElements*sizeof(IUnknown*));
                memset(pElements,0,requestedNumberOfElements*sizeof(IUnknown*));
                ULONG actual = 0;

                auto hr = pInterface->Next(1,pElements,&actual);
                if(actual)
                {
                    result.clear();
                    for(ULONG i= 0; i < actual; i++)
                    {
                        result.push_back(Unknown(pElements[i]));
                    }
                    return true;
                }
                return false;
            }
            EnumUnknown& Skip(ULONG numberOfElements)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Skip(numberOfElements);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            EnumUnknown& Reset( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Reset();
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }

            EnumUnknown Clone()
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IEnumUnknown* pClone = nullptr;
                auto hr = pInterface->Clone(&pClone);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return EnumUnknown(pClone);
            }
        };


        class EnumVARIANT : public Unknown
        {
        public:
            typedef Unknown Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(EnumVARIANT,Unknown,IEnumVARIANT,IUnknown)

            bool Next( VARIANT& theResult )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULONG actual = 0;
                auto hr = pInterface->Next(1,&theResult,&actual);
                if(actual)
                {
                    return true;
                }
                return false;
            }

            bool Next( ULONG requestedNumberOfElements, std::vector<Variant>& result)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                result.clear();
                result.resize(requestedNumberOfElements);
                ULONG actual = 0;

                auto hr = pInterface->Next(1,result.data(),&actual);
                if(actual)
                {
                    if(result.size() != actual)
                    {
                        result.resize(actual);
                    }
                    return true;
                }
                return false;
            }

            EnumVARIANT& Skip(ULONG numberOfElements)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Skip(numberOfElements);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            EnumVARIANT& Reset( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Reset();
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }

            EnumVARIANT Clone()
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IEnumVARIANT* pClone = nullptr;
                auto hr = pInterface->Clone(&pClone);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return EnumVARIANT(pClone);
            }
        
        };


        class SequentialStream : public Unknown
        {
        public:
            typedef Unknown Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(SequentialStream,Unknown,ISequentialStream,IUnknown)

            ULONG Read(void *pv, ULONG numberOfBytesToRead)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULONG result = 0;
                auto hr = pInterface->Read(pv, numberOfBytesToRead,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }

            ULONG Write(const void *pv, ULONG numberOfBytesToWrite)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULONG result = 0;
                auto hr = pInterface->Write(pv, numberOfBytesToWrite,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
        };

        class Stream : public SequentialStream
        {
        public:
            typedef SequentialStream Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(Stream,SequentialStream,IStream,ISequentialStream)

            unsigned long long Seek( long long numberOfBytesToMove, SeekOrigin seekOrigin )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULARGE_INTEGER result;
                result.QuadPart = 0;
                LARGE_INTEGER btm;
                btm.QuadPart = numberOfBytesToMove;
                auto hr = pInterface->Seek(btm,DWORD(seekOrigin),&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result.QuadPart;
            }
        
            Stream& SetSize( unsigned long long theNewSize )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULARGE_INTEGER nz;
                nz.QuadPart = theNewSize;
                auto hr = pInterface->SetSize(nz);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }

            Stream& CopyTo( IStream *pstm, unsigned long long numberOfBytesToCopy, unsigned long long* actualNumberOfBytesRead, unsigned long long* actualNumberOfBytesWritten)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULARGE_INTEGER nobtc,anobr,anobw;
                nobtc.QuadPart = numberOfBytesToCopy;
                anobr.QuadPart = 0;
                anobw.QuadPart = 0;
                auto hr = pInterface->CopyTo(pstm,nobtc,&anobr,&anobw);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                if(actualNumberOfBytesRead)
                {
                    *actualNumberOfBytesRead = anobr.QuadPart;
                }
                if(actualNumberOfBytesWritten)
                {
                    *actualNumberOfBytesWritten = anobw.QuadPart;
                }
                return *this;
            }
            Stream& CopyTo( IStream *pstm, unsigned long long numberOfBytesToCopy, unsigned long long& actualNumberOfBytesRead, unsigned long long& actualNumberOfBytesWritten)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULARGE_INTEGER nobtc,anobr,anobw;
                nobtc.QuadPart = numberOfBytesToCopy;
                anobr.QuadPart = 0;
                anobw.QuadPart = 0;
                auto hr = pInterface->CopyTo(pstm,nobtc,&anobr,&anobw);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                actualNumberOfBytesRead = anobr.QuadPart;
                actualNumberOfBytesWritten = anobw.QuadPart;
                
                return *this;
            }
            Stream& Commit(DWORD commitFlags = STGC_DEFAULT)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Commit(commitFlags);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            Stream& Revert( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Revert( );
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }

        
            Stream& LockRegion(unsigned long long offset, unsigned long long sizeOfRegion, DWORD lockType)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->LockRegion(*(ULARGE_INTEGER*)&offset, *(ULARGE_INTEGER*)&sizeOfRegion, lockType);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            Stream& UnlockRegion( unsigned long long offset, unsigned long long sizeOfRegion, DWORD lockType)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->UnlockRegion(*(ULARGE_INTEGER*)&offset, *(ULARGE_INTEGER*)&sizeOfRegion, lockType);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            Stream& Stat( STATSTG *pstatstg, DWORD grfStatFlag)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Stat(pstatstg, grfStatFlag);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            Stream Clone( )
            {
                HWIN_TRACE();
                IStream *result = nullptr;
                auto pInterface = GetInterface();
                auto hr = pInterface->Clone(&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return Stream(result);
            }

            Stream& WriteClassId(REFCLSID rclsid)
            {
                HWIN_TRACE();
                auto hr = WriteClassStm(*this,rclsid);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }

            CLSID ReadClassId( )
            {
                HWIN_TRACE();
                CLSID result = {0,};
                auto hr = ReadClassStm(*this,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                result;
            }
        };



    };
};

#endif // __HWINSHELL_H__